from typing import *

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

ax.plot([10, 10], [10, 10])


class RingEdgeMap:
    OL = 'ol'
    OR = 'or'
    OT = 'ot'
    OB = 'ob'
    OLT = 'olt'
    ORT = 'ort'
    OLB = 'olb'
    ORB = 'orb'
    IL = 'il'
    IR = 'ir'
    IT = 'it'
    IB = 'ib'
    ILT = 'ilt'
    IRT = 'irt'
    ILB = 'ilb'
    IRB = 'irb'
    C = 'c'


class Rect:
    def __init__(self, x0, y0, x1, y1, layer_id=None, ring_edge: str = None):
        self.ring_edge = ring_edge
        self.layer_id = layer_id
        self.y1 = y1
        self.x1 = x1
        self.y0 = y0
        self.x0 = x0

    def __str__(self):
        return self.x0, self.y0, self.x1, self.y1

    def __repr__(self):
        return f"Rect{repr((self.x0, self.y0, self.x1, self.y1))}"

    def __getitem__(self, item):
        return [(self.x0, self.y0), (self.x1, self.y1)][item]


# define Matplotlib figure and axis

pgs_map = {('DIFF', 0): [[(8380, -31630), (9380, 27370)], [(53600, -31630), (54600, 27370)], [(8380, 26370), (54600, 27370)], [(8380, -31630), (54600, -30630)]], ('NIMP', 0): [[(8360, -31650), (9400, 27390)], [(53580, -31650), (54620, 27390)], [(8360, 26350), (54620, 27390)], [(8360, -31650), (54620, -30610)]], ('SDNW', 0): [[(4680, -35330), (10380, 31070)], [(52600, -35330), (58300, 31070)], [(4680, 25370), (58300, 31070)], [(4680, -35330), (58300, -29630)]], ('NBL', 0): [[(4680, -35330), (58300, 31070)]], ('DNWELL', 0): [[(3180, -36830), (8290, 32570)], [(54690, -36830), (59800, 32570)], [(3180, 27460), (59800, 32570)], [(3180, -36830), (59800, -31720)]], ('NDT', 0): [[(4680, -35330), (10680, 31070)], [(52300, -35330), (58300, 31070)], [(4680, 25070), (58300, 31070)], [(4680, -35330), (58300, -29330)]], ('PCHSTBLK', 0): [[(680, -39330), (62300, 35070)]], ('TGOX50', 0): [[(4680, -35330), (58300, 31070)]], ('V40', 0): [[(3180, -36830), (59800, 32570)]], ('MET1', 0): [[(8380, -31630), (9380, 27370)], [(53600, -31630), (54600, 27370)], [(8380, 26370), (54600, 27370)], [(8380, -31630), (54600, -30630)]], ('CONT', 0): [[(8770, -30440), (8990, 26180)], [(53990, -30440), (54210, 26180)], [(9525, 26760), (53455, 26980)], [(9525, -31240), (53455, -31020)]]}
def group_pg_by_cut_direction(pgs):
    center_map = {}
    if len(pgs) == 1:
        return {'c': pgs[0]}
    for pg in pgs:
        center = (pg[1][0] + pg[0][0]) // 2, (pg[1][1] + pg[0][1]) // 2
        center_map[center] = pg
    ret = {}
    ret['t'] = center_map[max(center_map, key=lambda x: x[1])]
    ret['b'] = center_map[min(center_map, key=lambda x: x[1])]
    ret['l'] = center_map[min(center_map, key=lambda x: x[0])]
    ret['r'] = center_map[max(center_map, key=lambda x: x[0])]
    return ret


def get_cut_base_line(pgs):
    pg_map = group_pg_by_cut_direction(pgs)
    ret_map = {}
    for direction, pg in pg_map.items():
        if direction in ['t', 'b']:
            cut_line = 'y', (pg[1][1] + pg[0][1]) // 2
        elif direction in ['l', 'r']:
            cut_line = 'x', (pg[1][0] + pg[0][0]) // 2
        ret_map[direction] = cut_line
    return ret_map


def cut_pg(pg, cut_line: tuple) -> Dict:
    pg_lst = [('c', pg)]
    if cut_line[0] == 'x':
        if pg[0][0] < cut_line[1] < pg[1][0]:
            pg_lst = [('l', [(pg[0][0], pg[0][1]), (cut_line[1], pg[1][1])]), ('r', [(cut_line[1], pg[0][1]), (pg[1][0], pg[1][1])])]
        elif pg[0][0] >= cut_line[1]:
            pg_lst = [('r', pg)]
        elif cut_line[1] >= pg[1][0]:
            pg_lst = [('l', pg)]

    elif cut_line[0] == 'y':
        if pg[0][1] < cut_line[1] < pg[1][1]:
            pg_lst = [('b', [(pg[0][0], pg[0][1]), (pg[1][0], cut_line[1])]), ('t', [(pg[0][0], cut_line[1]), (pg[1][0], pg[1][1])])]
        elif pg[0][1] >= cut_line[1]:
            pg_lst = [('t', pg)]
        elif cut_line[1] >= pg[1][1]:
            pg_lst = [('b', pg)]
    return dict(pg_lst)


def split_pgs(grouped_pg_map: Dict[str, List], cut_line_map: Dict):
    ret = {}
    for direction, pg in grouped_pg_map.items():
        ret[direction] = {'c': pg}
        if direction in cut_line_map:
            ret[direction] = cut_pg(pg, cut_line_map[direction])
    return ret


def get_ring_polygon(pgs):
    if not pgs:
        return None
    if len(pgs) == 1:
        ret = {'outer': pgs[0], 'inner': None}
    else:
        o_x0 = min([pg[0][0] for pg in pgs])
        o_y0 = min([pg[0][1] for pg in pgs])
        o_x1 = max([pg[1][0] for pg in pgs])
        o_y1 = max([pg[1][1] for pg in pgs])

        i_x0 = min([pg[1][0] for pg in pgs])
        i_y0 = min([pg[1][1] for pg in pgs])
        i_x1 = max([pg[0][0] for pg in pgs])
        i_y1 = max([pg[0][1] for pg in pgs])
        ret = {'outer': ((o_x0, o_y0), (o_x1, o_y1)), 'inner': ((i_x0, i_y0), (i_x1, i_y1))}

    return ret


def split_polygon(pg_map: Dict, cut_line_map: Dict):
    ret = {'outer_bb': [],
           'inner_bb': []}

    cut_line_bb = (cut_line_map['l'][1], cut_line_map['b'][1]), (cut_line_map['r'][1], cut_line_map['t'][1])
    outer_bb = pg_map['outer']
    inner_bb = pg_map['inner']
    if outer_bb[0][0] < cut_line_bb[0][0]:
        ret['outer_bb'].append(Rect(outer_bb[0][0], cut_line_bb[0][1], cut_line_bb[0][0], cut_line_bb[1][1], ring_edge=RingEdgeMap.OL))
        ret['outer_bb'].append(Rect(cut_line_bb[1][0], cut_line_bb[0][1], outer_bb[1][0], cut_line_bb[1][1], ring_edge=RingEdgeMap.OR))
        ret['outer_bb'].append(Rect(cut_line_bb[0][0], cut_line_bb[1][1], cut_line_bb[1][0], outer_bb[1][1], ring_edge=RingEdgeMap.OT))
        ret['outer_bb'].append(Rect(cut_line_bb[0][0], outer_bb[0][1], cut_line_bb[1][0], cut_line_bb[0][1], ring_edge=RingEdgeMap.OB))

        ret['outer_bb'].append(Rect(outer_bb[0][0], cut_line_bb[1][1], cut_line_bb[0][0], outer_bb[1][1], ring_edge=RingEdgeMap.OLT))
        ret['outer_bb'].append(Rect(outer_bb[0][0], outer_bb[0][1], cut_line_bb[0][0], cut_line_bb[0][1], ring_edge=RingEdgeMap.OLB))
        ret['outer_bb'].append(Rect(cut_line_bb[1][0], cut_line_bb[1][1], outer_bb[1][0], outer_bb[1][1], ring_edge=RingEdgeMap.ORT))
        ret['outer_bb'].append(Rect(cut_line_bb[1][0], outer_bb[0][1], outer_bb[1][0], cut_line_bb[0][1], ring_edge=RingEdgeMap.ORB))

        # 铺满的ring
        if pg_map['inner']:
            inner_bb = pg_map['inner']
            ret['inner_bb'].append(Rect(cut_line_bb[0][0], cut_line_bb[0][1], cut_line_bb[1][0], inner_bb[0][1], ring_edge=RingEdgeMap.IB))
            ret['inner_bb'].append(Rect(inner_bb[1][0], cut_line_bb[0][1], cut_line_bb[1][0], cut_line_bb[1][1], ring_edge=RingEdgeMap.IR))
            ret['inner_bb'].append(Rect(cut_line_bb[0][0], inner_bb[1][1], cut_line_bb[1][0], cut_line_bb[1][1], ring_edge=RingEdgeMap.IT))
            ret['inner_bb'].append(Rect(cut_line_bb[0][0], cut_line_bb[0][1], inner_bb[0][0], cut_line_bb[1][1], ring_edge=RingEdgeMap.IL))
        else:
            ret['inner_bb'].append(Rect(*cut_line_bb[0], *cut_line_bb[1], ring_edge=RingEdgeMap.C))
    ret_lst = []
    for k, v in ret.items():
        ret_lst.extend(v)

    return ret_lst


base_cut_line = get_cut_base_line(pgs_map[('DIFF', 0)])

# for k, v in base_cut_line.items():
#     if k in ['t', 'b']:
#         plt.axhline(y=v[1])
#     else:
#         plt.axvline(x=v[1])

print(base_cut_line)
for layer, pgs in pgs_map.items():
    pg_map = get_ring_polygon(pgs)
    if not pg_map:continue
    ret = split_polygon(pg_map, base_cut_line)
    for rect in ret:
        edge:str = rect.ring_edge
        if edge.startswith('o'):
            c = 'red'
        else:
            c = 'blue'

        # ax.add_patch(Rectangle((rect.x0, rect.y0), rect.x1 - rect.x0, rect.y1 - rect.y0, fill=True, linewidth=1, color=c, alpha=0.01))

# plt.show()

pg_lst =  [((4870.0, -35220.0, 11060.0, 30960.0), True, '24-0'), ((11060.0, 25370.0, 14370.0, 30960.0), True, '24-0'), ((11060.0, -35220.0, 14370.0, -29630.0), True, '24-0'), ((14370.0, 25370.0, 18650.0, 30960.0), True, '24-0'), ((14370.0, -35220.0, 18650.0, -29630.0), True, '24-0'), ((17680.0, -29630.0, 18650.0, 25370.0), True, '24-0'), ((18650.0, 25370.0, 22930.0, 30960.0), True, '24-0'), ((18650.0, -35220.0, 22930.0, -29630.0), True, '24-0'), ((18650.0, -29630.0, 19620.0, 25370.0), True, '24-0'), ((22930.0, 25370.0, 27210.0, 30960.0), True, '24-0'), ((22930.0, -35220.0, 27210.0, -29630.0), True, '24-0'), ((26240.0, -29630.0, 27210.0, 25370.0), True, '24-0'), ((27210.0, 25370.0, 31490.0, 30960.0), True, '24-0'), ((27210.0, -35220.0, 31490.0, -29630.0), True, '24-0'), ((27210.0, -29630.0, 28180.0, 25370.0), True, '24-0'), ((31490.0, 25370.0, 35770.0, 30960.0), True, '24-0'), ((31490.0, -35220.0, 35770.0, -29630.0), True, '24-0'), ((34800.0, -29630.0, 35770.0, 25370.0), True, '24-0'), ((35770.0, 25370.0, 40050.0, 30960.0), True, '24-0'), ((35770.0, -35220.0, 40050.0, -29630.0), True, '24-0'), ((35770.0, -29630.0, 36740.0, 25370.0), True, '24-0'), ((40050.0, 25370.0, 44330.0, 30960.0), True, '24-0'), ((40050.0, -35220.0, 44330.0, -29630.0), True, '24-0'), ((43360.0, -29630.0, 44330.0, 25370.0), True, '24-0'), ((44330.0, 25370.0, 48610.0, 30960.0), True, '24-0'), ((44330.0, -35220.0, 48610.0, -29630.0), True, '24-0'), ((44330.0, -29630.0, 45300.0, 25370.0), True, '24-0'), ((51920.0, -35220.0, 58110.0, 30960.0), True, '24-0'), ((48610.0, 25370.0, 51920.0, 30960.0), True, '24-0'), ((48610.0, -35220.0, 51920.0, -29630.0), True, '24-0'), ((4680.0, -31130.0, 8880.0, 26870.0), False, '24-0'), ((54100.0, -31130.0, 58300.0, 26870.0), False, '24-0'), ((8880.0, 26870.0, 54100.0, 31070.0), False, '24-0'), ((8880.0, -35330.0, 54100.0, -31130.0), False, '24-0'), ((4680.0, 26870.0, 8880.0, 31070.0), False, '24-0'), ((4680.0, -35330.0, 8880.0, -31130.0), False, '24-0'), ((54100.0, 26870.0, 58300.0, 31070.0), False, '24-0'), ((54100.0, -35330.0, 58300.0, -31130.0), False, '24-0'), ((8880.0, -31130.0, 54100.0, -29630.0), True, '24-0'), ((52600.0, -31130.0, 54100.0, 26870.0), True, '24-0'), ((8880.0, 25370.0, 54100.0, 26870.0), True, '24-0'), ((8880.0, -31130.0, 10380.0, 26870.0), True, '24-0')]

# [(pg.bounds, pg.visible, layer) for layer, pgs in self.unit_array[0][1].ring_data.items() for pg in pgs][60:]
for i in pg_lst:
    rect = i[0]
    visible = i[1]
    print(rect)
    print(rect[0])
    c = 'red'
    if not visible:
        c = 'blue'
    ax.add_patch(Rectangle((rect[0], rect[1]), rect[2] - rect[0], rect[3] - rect[1], fill=True, linewidth=1, color=c, alpha=0.1))

plt.show()